﻿using Modbus.Device;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Demo.Public.Modbsus
{
    /// <summary>
    /// Helper class for Modbus communication.
    /// </summary>
    public  class ModbusHelper
    {
        private static ModbusHelper _Instance = null;
        private IModbusMaster _modbusMaster = null;

        /// <summary>
        /// Gets or sets the unique instance of the ModbusHelper class.
        /// </summary>
        public static ModbusHelper Instance
        {
            get
            {
                if (null == _Instance)
                {
                    _Instance = new ModbusHelper();
                }
                return _Instance;
            }
            set { _Instance = value; }
        }

        /// <summary>
        /// Private default constructor to prevent external instantiation.
        /// </summary>
        private ModbusHelper()
        {
        }

        /// <summary>
        /// Event triggered when data is received.
        /// </summary>
        public Action<byte[]> OnDataRecived = null;

        private SerialPort _serialPort = new SerialPort();

        /// <summary>
        /// Opens the serial port with the specified settings.
        /// </summary>
        /// <param name="portName">The name of the port to open.</param>
        /// <param name="baudRate">The baud rate for the port.</param>
        /// <param name="parity">The parity for the port.</param>
        /// <param name="dataBits">The number of data bits for the port.</param>
        /// <param name="stopBits">The stop bits for the port.</param>
        /// <returns>True if the port was opened successfully, otherwise false.</returns>
        public bool Open(string portName, int baudRate = 9600, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One)
        {
            try
            {
                if (_serialPort.IsOpen)
                {
                    _serialPort.Close();
                }
                _serialPort.PortName = portName;
                _serialPort.BaudRate = baudRate;
                _serialPort.Parity = parity;
                _serialPort.DataBits = dataBits;
                _serialPort.StopBits = stopBits;
                _serialPort.Open();

                _modbusMaster = ModbusSerialMaster.CreateRtu(_serialPort);
                _modbusMaster.Transport.ReadTimeout = 1000;
                _modbusMaster.Transport.WriteTimeout = 1000;
                _modbusMaster.Transport.Retries = 3;

                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        /// <summary>
        /// Handles the DataReceived event of the serial port.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="SerialDataReceivedEventArgs"/> instance containing the event data.</param>
        private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            while (_serialPort.IsOpen && _serialPort.BytesToRead > 0)
            {
                byte[] buffer = new byte[_serialPort.BytesToRead];
                _serialPort.Read(buffer, 0, buffer.Length);
                OnDataRecived?.Invoke(buffer);
            }
        }

        /// <summary>
        /// Closes the serial port.
        /// </summary>
        /// <returns>True if the port was closed successfully, otherwise false.</returns>
        public bool Close()
        {
            try
            {
                if (_serialPort.IsOpen)
                {
                    _serialPort.DataReceived -= _serialPort_DataReceived;
                    if (_modbusMaster != null)
                    {
                        _modbusMaster.Dispose();
                    }
                    _serialPort.Close();
                }
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        /// <summary>
        /// Reads temperature and humidity data asynchronously.
        /// </summary>
        /// <param name="slaveID">The slave ID.</param>
        /// <param name="startAdd">The start address.</param>
        /// <param name="Count">The number of registers to read.</param>
        /// <returns>An array of ushort containing the read data.</returns>
        public async Task<ushort[]> ReadTHDataAsync(byte slaveID, ushort startAdd, ushort Count)
        {
            if (_serialPort.IsOpen == false && _modbusMaster == null)
            {
                return null;
            }
            return await _modbusMaster.ReadInputRegistersAsync(slaveID, startAdd, Count);
        }
    }
}